package com.jh.loadbalance.client.interceptor;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpRequest;
import org.springframework.http.HttpStatus;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Random;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Component
public class LoadBalanceClientHttpRequestInterceptor implements ClientHttpRequestInterceptor {

    @Autowired
    private DiscoveryClient discoveryClient;

    private volatile Map<String, Set<String>> targets = new HashMap<>();

    @Scheduled(fixedRate = 10 * 1000) // 10秒更新一次服务列表
    public void getAllTargets() {
        Optional.ofNullable(discoveryClient.getServices())
                .filter(list -> !list.isEmpty())
                .ifPresent(services -> {
                    Map<String, Set<String>> oldTargets = targets;
                    Map<String, Set<String>> newTargets = new HashMap<>(8);
                    services.forEach(temp -> {
                        // 将自己都放了进去，有点问题
                        List<ServiceInstance> instances = discoveryClient.getInstances(temp);
                        Set<String> instanceUrls = instances.stream().map(instance ->
                                (instance.isSecure() ? "https://" : "http://") + instance.getHost() + ":" + instance.getPort())
                                .collect(Collectors.toSet());
                        newTargets.put(temp, instanceUrls);
                    });

                    targets = newTargets;
                    oldTargets.clear();
                });
    }

    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {

        URI uri = request.getURI();
        String uriStr = uri.getPath();
        String[] splitArray = uriStr.substring(1).split("/");
        String applicationName = splitArray[0];
        String path = Stream.of(splitArray).filter(temp -> !applicationName.equals(temp)).collect(Collectors.joining("/"));

        List<String> nowTargets = new ArrayList<>(this.targets.get(applicationName));
        int size = nowTargets.size();
        int index = new Random().nextInt(size);
        String query = uri.getQuery();
        String url = nowTargets.get(index) + "/" + path + (StringUtils.hasLength(query) ? "?" + query : "");

        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.add("Content-Type", "text/html; charset=UTF-8");

        /*RestTemplate restTemplate = new RestTemplate();
        String response = restTemplate.getForObject(url, String.class);
        ByteArrayInputStream inputStream = new ByteArrayInputStream(response.getBytes("utf-8"));*/

        URL urlEntity = new URL(url);
        URLConnection connection = urlEntity.openConnection();
        InputStream inputStream = connection.getInputStream();

        return new SimpleClientHttpResponse(inputStream, httpHeaders);
    }

    private static class SimpleClientHttpResponse implements ClientHttpResponse {

        private InputStream inputStream;

        private HttpHeaders httpHeaders;

        public SimpleClientHttpResponse(InputStream inputStream, HttpHeaders httpHeaders) {
            this.inputStream = inputStream;
            this.httpHeaders = httpHeaders;
        }

        @Override
        public HttpStatus getStatusCode() throws IOException {
            return HttpStatus.OK;
        }

        @Override
        public int getRawStatusCode() throws IOException {
            return HttpStatus.OK.value();
        }

        @Override
        public String getStatusText() throws IOException {
            return HttpStatus.OK.getReasonPhrase();
        }

        @Override
        public void close() {

        }

        @Override
        public InputStream getBody() throws IOException {
            return this.inputStream;
        }

        @Override
        public HttpHeaders getHeaders() {
            return this.httpHeaders;
        }
    }

}
